package com.study.cloud.apilog.response;

import com.study.cloud.apilog.GatewayApiLog;
import com.study.cloud.apilog.MyCachedBodyOutputMessage;
import com.study.cloud.utils.MediaTypeUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @Date 2022/5/17
 * @Author Cliff
 * desc
 */
@Component
public class ComplexResponseBodyImpl extends AbstractResponseBody {


    @Override
    public boolean isSupport(MediaType mediaType) {
        return mediaType != null
                && (MediaTypeUtil.isFormDataType(mediaType)
                || MediaTypeUtil.isWWWFormType(mediaType)
                || MediaTypeUtil.isApplicationJsonType(mediaType));
    }

    /**
     * 解决 request body 只能读取一次问题，
     * 参考: org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory
     *
     * @param exchange
     * @param chain
     * @param gatewayLog
     * @param request
     * @return
     */
    @Override
    public Mono<Void> handler(ServerWebExchange exchange, GatewayFilterChain chain,
                              GatewayApiLog gatewayLog, Object request) {

        BodyInserter bodyInserter = (BodyInserter) request;
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(exchange.getRequest().getHeaders());
        // the new content type will be computed by bodyInserter
        // and then set in the request decorator
        headers.remove(HttpHeaders.CONTENT_LENGTH);

        MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headers);

        return bodyInserter.insert(outputMessage, new BodyInserterContext())
                .then(Mono.defer(() -> {
                    // 重新封装请求
                    ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage);

                    // 记录响应日志
                    ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, gatewayLog);

                    // 记录普通的
                    return getThen(exchange, chain, gatewayLog, decoratedRequest, decoratedResponse);
                }));

    }
}
